{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Training quantum embedding kernels for classification\n",
    "\n",
    "In this tutorial we will dive deeper into the the concept of quantum embedding kernels, or quantum kernels. We will train a quantum kernel based on a similarity measure called the kernel-target alignment and see how the training improves the performance of it as a classifier. We will demonstrate how to use Covalent to orchestrate the whole workflow composed of multiple smaller subtasks. Once the workflow is dispatched, we will be able to track the results of each subtask through the Covalent result manager. In the meantime, all the subtasks constructed can be called as normal Python functions, giving users more flexibility in testing and debugging. For more details about the quantum embedding kernels, we refer the reader to this PennyLane demo: [Training and evaluating quantum kernels](https://pennylane.ai/qml/demos/tutorial_kernels_module.html#training-qeks), which this tutorial is based on."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "In addition to Covalent, one first needs to install the following packages in order to fully reproduce the results of this tutorial."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "covalent\n",
      "matplotlib==3.5.1\n",
      "pennylane==0.23.1\n",
      "scikit-learn==1.1.1\n"
     ]
    }
   ],
   "source": [
    "with open(\"./requirements.txt\", \"r\") as file:\n",
    "    for line in file:\n",
    "        print(line.rstrip())\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Install required packages\n",
    "# !pip install -r ./requirements.txt\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let us begin by running `covalent start` in a terminal to start the Covalent server. Next we import the necessary packages."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "import pennylane as qml\n",
    "from pennylane import numpy as np\n",
    "import covalent as ct\n",
    "import matplotlib.pyplot as plt\n",
    "from matplotlib.colors import ListedColormap\n",
    "from sklearn.datasets import make_blobs\n",
    "from sklearn.model_selection import train_test_split\n",
    "from sklearn.svm import SVC\n",
    "from sklearn.inspection import DecisionBoundaryDisplay\n",
    "\n",
    "np.random.seed(2022)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Defining the quantum kernel\n",
    "\n",
    "We first generate a dataset with two features and two classes for our classification task using `scikit-learn`'s `make_blobs` function. For this tutorial, we will make a toy dataset that contain 18 points as 9 \"blobs\", or clusters, arranged in a checkerboard fashion. We then do a train/test split of ratio 8:2. Moreover, we include the above two steps in the Covalent workflow as subtasks by adding the `electron` decorator on top of the respective functions."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "@ct.electron\n",
    "def get_data(n_samples=18):\n",
    "    centers = [(1, 1), (1, 2), (1, 3), (2, 1), (2, 2), (2, 3), (3, 1), (3, 2), (3, 3)]\n",
    "    X, y = make_blobs(\n",
    "        n_samples=n_samples, n_features=2, centers=centers, cluster_std=0.1, shuffle=False\n",
    "    )\n",
    "    # rescale labels to be -1, 1\n",
    "    mapping = {0: -1, 1: 1, 2: -1, 3: 1, 4: -1, 5: 1, 6: -1, 7: 1, 8: -1}\n",
    "    y = np.array([mapping[i] for i in y])\n",
    "    X = X.astype(np.float32)\n",
    "    y = y.astype(int)\n",
    "    return X, y\n",
    "\n",
    "\n",
    "@ct.electron\n",
    "def split_train_test_data(X, y, test_size=0.2):\n",
    "    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=test_size, random_state=3)\n",
    "    return X_train, X_test, y_train, y_test\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "As mentioned in the beginning, these subtasks can be called as normal Python functions. So we can plot the dataset for visualization."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAagAAAEYCAYAAAAJeGK1AAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjUuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/YYfK9AAAACXBIWXMAAAsTAAALEwEAmpwYAAAjkElEQVR4nO3de3RU5b3/8fck4ZILiXJTTChpDZeQkIwwTfAoRKAIRMnhzqLhViMBly32iCw9SwW1FmspFuxhgWmVFuwRNQdLi6hVIWgh+UlAQEEkoIEQQBJsgECASWb//tgSiMkwIWRmdpjPa62sYfbs7Pm61zifPPt59vPYDMMwEBERsZggfxcgIiLSEAWUiIhYkgJKREQsSQElIiKWpIASERFLCvF3AVerY8eOxMbG+rsMERFpJsXFxZSXl9fb3uICKjY2lsLCQn+XISIizcThcDS4XZf4RETEkhRQIiJiSQooERGxJAWUiIhYkgJKREQsqcWN4hOxGpcLNm2Cr74Cux369fN3RSLXBwWUyDU4fhzuugtKSsAwzJ/+/eHtt6FtW39XJ9Ky6RKfyDXIyoKiIqishDNn4OxZ2LIFnnnG35WJtHwKKJEmOncO3nsPqqvrb3/lFf/UJHI9UUCJNFF1tXlJryEXLvi2FpHrkQJKpIkiIiA5uf72kBDIyPB9PSLXGw2SuNy+fbBmjfln8Zgx0LOnvysSi1uxAgYMMFtMVVUQHg433AC/+Y2/KxNp+RRQF/3udzBv3qXrNr/6lfn8scf8XZlYWJ8+sH+/GVRffAGpqTB5shlUInJtbIbh7iq6NTkcjuafzfzAAUhMNHu3LxcaCjt3Qvfuzft+IiJSy933uvqgAP72N/Nuy++rroa33vJ5OSIi4sWAOnfuHCkpKSQnJ5OQkMD8+fPr7XP+/HkmTpxIXFwcqampFBcXe6ucK7PZzJ/GbhcREa/zWkC1adOGDRs2sHPnTnbs2MG7775LQUFBnX1efvllbrzxRvbv389//dd/8eijj3qrnCsbM6bhIAoKgrFjfV+PiIiVFRfDrFmQkGAOWd2yxStv47WAstlsREREAOB0OnE6ndi+FwJr165l2rRpAIwbN44PP/wQv3SJxcaagyTatoU2bcyftm3NoVg/+pHv6xERsar9+81JJ19+GfbsgX/8A4YOhdzcZn8rr/ZB1dTUYLfb6dy5M0OHDiU1NbXO66WlpXTt2hWAkJAQoqKiOHHihDdLcu/BB2HvXnj+eTOYvvgCHnrIP7WIiFjV44/D6dN1p1A5e9b8Dm2oL/8aeHWYeXBwMDt27KCiooLRo0fz+eefk5iYeNXHycnJIScnB4CysrLmLvOSbt0USiIiV7JpU8NBdPo0HDkCMTHN9lY+GcV3ww03MGjQIN59990626OjoykpKQGgurqakydP0qFDh3q/n52dTWFhIYWFhXTq1MkXJYuISEM6d254u8tl3qXejLwWUGVlZVRUVABQVVXF+++/T69eversk5GRwV/+8hcAcnNzGTx4cL1+KhERsZBHH4WwsLrb2rY1B5R9N+6guXjtEt/Ro0eZNm0aNTU1uFwuJkyYwL333su8efNwOBxkZGSQlZXFlClTiIuLo3379qxevdpb5YiISHP46U/NyQ1+8xto1cqc52vECPiuG6Y5aSYJERG5epWV8OWXEB0NN998TYdy972uufhEROTqRURAv35efQtNdSQiIpakgBIREUtSQImIiCWpD0pExF++/Rb+/GfYvRscDpgypdmHardkakGJNKPNmyEqypx7eNIk8+Z6kQbt3QtxcfDEE/DKKzB3LvToAaWl/q7MMhRQIs3kb3+DO++EU6fMm+1Xr4bISHA6/V2ZWNKMGVBRAVVV5vMzZ+D4cXjkEb+WZSUKKJFmYBgwerR532JNDXzzjXnVBuC3v/VvbWJBFy5Afr75wblcTY05O7gACiiRZnH2rPn42mvmMmIAvXubjytW+KcmsbCgoEsflO9r3dq3tViYAkqkGVycmuy55y5tO37cfPzJT3xfj1hcSAj853+aTe7LtWkD362RJwookWZhs5nhtG2bucblnDlw003mawsX+rc2sajly81BERER5l844eHmSL5nn/V3ZZahYeYizeTRR82rNo8+Ci+8YF6p2bMH2rXzd2ViSR06wGefwUcfwb59kJQEKSnmXzsCBOpksTU15ofA3TVgERHxGXff64H1DX3ggNkh0KaNuX7JxIngryXmRUTkigLnEt+pU9C/v3nntstltqLeesscC7xrl1pTIiIWEzjfyqtWmWOBXa5L25xOOHgQ8vL8VpaIiDQscALqs88u3axyuZoac9EtERGxlMAJqL59zWGc3xccDImJvq9HRESuKHACKjPTnBgtOPjSttatoVcvcwI1ERGxlMAJqPBw2LrVnDAtNNS8OSUrCzZs0H0HIiIWFDij+ACio+HNN/1dhYiINELgtKBERKRFUUCJiIglKaBERMSSFFAiImJJCigREbEkBZSIiFiSAkpERCxJASUiIpakgBIREUtSQImIiCUpoERExJIUUCIiYkkKKBERsSQFlIiIWJICSkRELEkBJSIilqSAEhERS1JAiYiIJXktoEpKShg0aBC9e/cmISGBJUuW1NsnLy+PqKgo7HY7drudZ555xlvliIhICxPitQOHhLBo0SL69u3L6dOn6devH0OHDqV379519hswYADr1q3zVhkiItJCea0F1aVLF/r27QtAu3btiI+Pp7S01FtvJyIi1xmf9EEVFxfz6aefkpqaWu+1/Px8kpOTGTFiBLt3727w93NycnA4HDgcDsrKyrxdroiIWIDNMAzDm29QWVlJWloajz/+OGPGjKnz2qlTpwgKCiIiIoL169fz0EMPUVRUdMXjORwOCgsLvVmyiIj4kLvvda+2oJxOJ2PHjiUzM7NeOAFERkYSEREBQHp6Ok6nk/Lycm+WJCIiLYTXAsowDLKysoiPj+fhhx9ucJ9jx45xsQH3ySef4HK56NChg7dKEhGRFsRro/g2b97MqlWr6NOnD3a7HYAFCxZw6NAhAGbNmkVubi7Lli0jJCSE0NBQVq9ejc1m81ZJIiLSgni9D6q5qQ9KROT64pc+KBERkaZSQImIiCUpoERExJIUUCIiYkkKKBERsSQFlIiIWJICSkRELEkBJSIiltSogDp48CAffPABAFVVVZw+fdqrRYmIiHgMqD/+8Y+MGzeOmTNnAnD48GFGjRrl7bpERCTAeQyopUuXsnnzZiIjIwHo3r07x48f93phIiIS2DwGVJs2bWjdunXt8+rqak3oKiIiXucxoNLS0liwYAFVVVW8//77jB8/npEjR/qiNhERCWAeA+r555+nU6dO9OnTh5deeon09HSeffZZX9QmIiIB7IrrQdXU1JCQkMDevXuZMWOGr2oSERG5cgsqODiYnj171i4yKCIi4iseV9T997//TUJCAikpKYSHh9du//vf/+7VwkREJLB5DKhf/epXvqhDRESkDo8BlZaW5os6RERE6vAYUO3atau97+nChQs4nU7Cw8M5deqU14sTEZHA5TGgLp93zzAM1q5dS0FBgVeLEhERuarZzG02G6NG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",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "X, y = get_data()\n",
    "X_train, X_test, y_train, y_test = split_train_test_data(X, y)\n",
    "\n",
    "cm = plt.cm.RdBu\n",
    "cm_bright = ListedColormap([\"#FF0000\", \"#0000FF\"])\n",
    "fig, ax = plt.subplots(1, 1, figsize=(6, 4), facecolor=\"w\")\n",
    "ax.scatter(X_train[:, 0], X_train[:, 1], c=y_train, cmap=cm_bright)\n",
    "ax.scatter(X_test[:, 0], X_test[:, 1], c=y_test, marker=\"$\\u25EF$\", cmap=cm_bright)\n",
    "ax.set_xlabel(\"First feature\")\n",
    "ax.set_ylabel(\"Second feature\")\n",
    "plt.tight_layout()\n",
    "plt.show()\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Next, we construct the quantum embedding kernel by building a *data reuploading* quantum variational circuit based on the ansatz given in [Hubregtsen et al. 2021](https://arxiv.org/abs/2105.02276). Specifically, this ansatz uses a repeating layer architecture, where each layer embeds the 2 features into 5 qubits through the angles of $R_Z$ gates. The variational parameters correspond to the angles of a series of $R_Y$ and controlled-$R_Z$ gates. The ansatz diagram of one layer is shown below.\n",
    "\n",
    "<div align=\"center\">\n",
    "<img src=\"assets/qek_circuit.png\" style=\"width: 40%; height: 40%\"/>\n",
    "</div>\n",
    "\n",
    "A quantum kernel is given by the mutual overlap of the two data-embedding quantum states, i.e.,\n",
    "\\begin{equation}\n",
    "k(x_1, x_2) = \\vert{\\langle x_2, \\boldsymbol{\\theta} \\vert x_1, \\boldsymbol{\\theta} \\rangle} \\vert^2,\n",
    "\\end{equation}\n",
    "where $\\boldsymbol{\\theta}$ denotes the variational parameters. Therefore, to define a quantum kernel function, we construct the following subtasks with Covalent:\n",
    "\n",
    "* `layer` - Build the quantum circuit for each layer based on the embedding ansatz given above.\n",
    "* `ansatz` - Construct the full ansatz circuit with an arbitrary number of layers.\n",
    "* `adjoint_ansatz` - Get the Hermitian adjoint of the ansatz circuit.\n",
    "* `kernel_circuit` - Construct the quantum embedding kernel circuit from `ansatz` and `adjoint_ansatz`.\n",
    "* `kernel` - Return the quantum kernel function based on `kernel_circuit`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "@ct.electron\n",
    "def layer(x, params, wires, i0=0, inc=1):\n",
    "    i = i0\n",
    "    for j, wire in enumerate(wires):\n",
    "        qml.Hadamard(wires=[wire])\n",
    "        qml.RZ(x[i % len(x)], wires=[wire])\n",
    "        i += inc\n",
    "        qml.RY(params[0, j], wires=[wire])\n",
    "\n",
    "    qml.broadcast(unitary=qml.CRZ, pattern=\"ring\", wires=wires, parameters=params[1])\n",
    "\n",
    "\n",
    "@ct.electron\n",
    "def ansatz(x, params, wires):\n",
    "    for j, layer_params in enumerate(params):\n",
    "        layer(x, layer_params, wires, i0=j * len(wires))\n",
    "\n",
    "\n",
    "@ct.electron\n",
    "def adjoint_ansatz(ansatz):\n",
    "    return qml.adjoint(ansatz)\n",
    "\n",
    "\n",
    "@ct.electron\n",
    "def kernel_circuit(params):\n",
    "    dev = qml.device(\n",
    "        \"default.qubit\", wires=params.shape[2], shots=None\n",
    "    )  # number of wires corresponds to the third dimension of params\n",
    "    wires = dev.wires.tolist()\n",
    "\n",
    "    @qml.qnode(dev)\n",
    "    def circuit(x1, x2, params):\n",
    "        ansatz(x1, params, wires=wires)\n",
    "        adjoint_ansatz(ansatz)(x2, params, wires=wires)\n",
    "        return qml.probs(wires=wires)\n",
    "\n",
    "    return circuit\n",
    "\n",
    "\n",
    "@ct.electron\n",
    "def kernel(x1, x2, params):\n",
    "    return kernel_circuit(params)(x1, x2, params)[0]\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now we are in a position to build a support vector machine (SVM) classifier based on the quantum kernel and evaluate the accuracy of its predictions. It consists of three subtasks:\n",
    "\n",
    "* `init_params` - Randomly initialize the variational parameters.\n",
    "* `svm` - Build the SVM classifier using the quantum kernel.\n",
    "* `get_accuracy` - Evaluate the accuracy of the classifier on a given dataset.\n",
    "\n",
    "To accomplish the second subtask, we first use PennyLane's `qml.kernels.kernel_matrix` function to get the kernel matrix of a full dataset (not just between two data points) based on the given parameters. The kernel matrix is then fed into `scikit-learn`'s support vector classifier (`SVC`), which allows us to build a quantum SVM estimator using the training data."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "@ct.electron\n",
    "def init_params(num_wires, num_layers):\n",
    "    return np.random.uniform(0, 2 * np.pi, (num_layers, 2, num_wires), requires_grad=True)\n",
    "\n",
    "\n",
    "@ct.electron\n",
    "def svm(X, y, params):\n",
    "    kernel_input = lambda x1, x2: kernel(x1, x2, params)\n",
    "    svm = SVC(kernel=lambda x1, x2: qml.kernels.kernel_matrix(x1, x2, kernel_input))\n",
    "    svm.fit(X, y)\n",
    "    return svm\n",
    "\n",
    "\n",
    "@ct.electron\n",
    "def calc_accuracy(classifier, X, y_true):\n",
    "    return 1 - np.count_nonzero(classifier.predict(X) - y_true) / len(y_true)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Finally, we can bundle all the subtasks discussed above into a single workflow function called `init_qsvm` with the `lattice` decorator on top. This function will return the quantum SVM classifier based on randomly initialized parameters, the training/testing data along with their corresponding labels, and the training/testing accuracies. For this tutorial, we will be using 3 layers of the embedding ansatz."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "@ct.lattice\n",
    "def init_qsvm(n_samples=18, num_wires=5, num_layers=3):\n",
    "    X, y = get_data(n_samples)\n",
    "    X_train, X_test, y_train, y_test = split_train_test_data(X, y)\n",
    "    params = init_params(num_wires, num_layers)\n",
    "    classifier = svm(X_train, y_train, params)\n",
    "    train_acc = calc_accuracy(classifier=classifier, X=X_train, y_true=y_train)\n",
    "    test_acc = calc_accuracy(classifier=classifier, X=X_test, y_true=y_test)\n",
    "    return classifier, X_train, y_train, X_test, y_test, train_acc, test_acc\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The workflow is then dispatched to the Covalent server and the results are retrievable once the workflow is finished. One can also track all the results through the Covalent result manager at [http://localhost:48008](http://localhost:48008)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "dispatch_id = ct.dispatch(init_qsvm)(18, 5, 3)\n",
    "result = ct.get_result(dispatch_id=dispatch_id, wait=True)\n",
    "qsvm, X_train, y_train, X_test, y_test, train_acc, test_acc = result.result\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Train accuracy: 0.9285714285714286\n",
      "Test accuracy: 0.75\n"
     ]
    }
   ],
   "source": [
    "print(f\"Train accuracy: {train_acc}\")\n",
    "print(f\"Test accuracy: {test_acc}\")\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Furthermore, we can also plot the decision boundaries of our initial SVM classifier for better visualization."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "disp_init = DecisionBoundaryDisplay.from_estimator(\n",
    "    qsvm, X, grid_resolution=30, response_method=\"predict\", alpha=0.5, cmap=cm\n",
    ")\n",
    "disp_init.ax_.scatter(X_train[:, 0], X_train[:, 1], c=y_train, cmap=cm_bright)\n",
    "disp_init.ax_.scatter(X_test[:, 0], X_test[:, 1], c=y_test, cmap=cm_bright, marker=\"$\\u25EF$\")\n",
    "plt.show()\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We see that with a random set of parameters our initial SVM classifier performs pretty well on the small dataset used here. The accuracy is about 93% on the training set, while on the testing set it is 75%. However, it is clear from the decision-boundary plot that the classifier failed to correctly classify one of the clusters (labelled in blue). In the next section, we will train the quantum kernel to find a better set of parameters which will hopefully improve the accuracy of the classifier on both the training and testing datasets."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Training the quantum kernel\n",
    "\n",
    "The *kernel alignment* is a similarity measure between two kernels with kernel matrices $K_1$ and $K_2$, which is defined as\n",
    "\\begin{equation}\n",
    "\\text{KA}(K_1, K_2)\\equiv \\frac{\\text{Tr}(K_1 K_2)}{\\sqrt{\\text{Tr}(K_1^2) \\text{Tr}(K_2^2)}}.\n",
    "\\end{equation}\n",
    "We will train the quantum SVM classifier by optimizing the *kernel-target alignment*, which compares the similarity predicted by the quantum kernel to the actual labels of the training data. In particular, any two training data points define an ideal kernel function given by\n",
    "\\begin{equation}\n",
    "k_{\\textbf{y}}(\\textbf{x}_i, \\textbf{x}_j) = y_i y_j,\n",
    "\\end{equation}\n",
    "where $y_{i,j}$ are the labels associated with the data points $\\textbf{x}_{i,j}$. The assigned kernel is thus $+1$ if both data points are in the same class and $-1$ if they belong to different classes. Then the kernel matrix is given by the outer product $\\textbf{y}\\textbf{y}^T$. The kernel-target alignment is therefore defined as the kernel alignment of the kernel matrix $K$ generated by the quantum kernel and the ideal kernel matrix $\\textbf{y}\\textbf{y}^T$:\n",
    "\\begin{equation}\n",
    "\\text{KTA}_{\\textbf{y}}(K) = \\frac{\\text{Tr}(K_1 \\textbf{y}\\textbf{y}^T)}{\\sqrt{\\text{Tr}(K^2) \\text{Tr}((\\textbf{y}\\textbf{y}^T)^2)}}.\n",
    "\\end{equation}\n",
    "The training then aims to find an optimal set of variational parameters that maximizes the kernel-target alignment."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "In the following, we will construct three subtasks as `electron` in Covalent for training the quantum embedding kernel:\n",
    "\n",
    "* `target_alignment` - Compute the kernel-target alignment of the quantum kernel for a given labelled dataset.\n",
    "* `get_optimizer` - Get the optimizer used for optimizing the kernel-target alignment. Here we choose the `AdagradOptimizer` from PennyLane.\n",
    "* `training` - Train the quantum embedding kernel by optimizing the kernel-target alignment. During each training step, instead of optimizing based on all of the training data, we will use a random subset of the data with 5 data points to reduce the computational complexity."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [],
   "source": [
    "@ct.electron\n",
    "def target_alignment(\n",
    "    X,\n",
    "    Y,\n",
    "    kernel,\n",
    "    assume_normalized_kernel=False,\n",
    "    rescale_class_labels=True,\n",
    "):\n",
    "    K = qml.kernels.square_kernel_matrix(\n",
    "        X,\n",
    "        kernel,\n",
    "        assume_normalized_kernel=assume_normalized_kernel,\n",
    "    )\n",
    "\n",
    "    if rescale_class_labels:\n",
    "        nplus = np.count_nonzero(np.array(Y) == 1)\n",
    "        nminus = len(Y) - nplus\n",
    "        _Y = np.array([y / nplus if y == 1 else y / nminus for y in Y])\n",
    "    else:\n",
    "        _Y = np.array(Y)\n",
    "\n",
    "    T = np.outer(_Y, _Y)\n",
    "    inner_product = np.sum(K * T)\n",
    "    norm = np.sqrt(np.sum(K * K) * np.sum(T * T))\n",
    "    inner_product = inner_product / norm\n",
    "\n",
    "    return inner_product\n",
    "\n",
    "\n",
    "@ct.electron\n",
    "def get_optimizer():\n",
    "    return qml.AdagradOptimizer(0.4)\n",
    "\n",
    "\n",
    "@ct.electron\n",
    "def training(X, Y, init_params, opt, steps):\n",
    "    params = init_params\n",
    "    KTAs = []\n",
    "    for i in range(steps):\n",
    "        # Randomly choose subset of data points to compute the KTA on.\n",
    "        subset = np.random.choice(list(range(len(X))), 5)\n",
    "        # Define the cost function for optimization\n",
    "        cost = lambda _params: -target_alignment(\n",
    "            X[subset],\n",
    "            Y[subset],\n",
    "            lambda x1, x2: kernel(x1, x2, _params),\n",
    "            assume_normalized_kernel=True,\n",
    "        )\n",
    "        # Optimization step\n",
    "        params = opt.step(cost, params)\n",
    "        KTAs.append(\n",
    "            target_alignment(\n",
    "                X, Y, lambda x1, x2: kernel(x1, x2, params), assume_normalized_kernel=True\n",
    "            )\n",
    "        )\n",
    "\n",
    "    return params, KTAs\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Similar to the previous case, we construct a full workflow called `trained_qsvm` by combining all the necessary subtasks for building a trained quantum SVM classifier. Note that we also track the kernel-target alignment at each training step through one of the returns `KTAs`. We will limit ourselves to 300 training steps in this demo. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [],
   "source": [
    "@ct.lattice\n",
    "def trained_qsvm(n_samples=18, num_wires=5, num_layers=3, steps=300):\n",
    "    X, y = get_data(n_samples)\n",
    "    X_train, X_test, y_train, y_test = split_train_test_data(X, y)\n",
    "    params = init_params(num_wires, num_layers)\n",
    "    opt = get_optimizer()\n",
    "    opt_params, KTAs = training(X_train, y_train, params, opt, steps)\n",
    "    opt_classifier = svm(X_train, y_train, opt_params)\n",
    "    train_acc = calc_accuracy(classifier=opt_classifier, X=X_train, y_true=y_train)\n",
    "    test_acc = calc_accuracy(classifier=opt_classifier, X=X_test, y_true=y_test)\n",
    "    return opt_classifier, KTAs, X_train, y_train, X_test, y_test, train_acc, test_acc\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [],
   "source": [
    "dispatch_id = ct.dispatch(trained_qsvm)(18, 5, 3, 300)\n",
    "result = ct.get_result(dispatch_id=dispatch_id, wait=True)\n",
    "opt_qsvm, KTAs, X_train, y_train, X_test, y_test, train_acc, test_acc = result.result\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This is what the full workflow in Covalent UI looks like:\n",
    "<div align=\"center\">\n",
    "<img src=\"assets/qek_workflow.png\" style=\"width: 95%; height: 95%\"/>\n",
    "</div>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Once the results are retrieved, we first take a look at the history of the kernel-target alignment. We can see that it increased from 0.2 to around 0.33 after 100 steps and more or less plateaued afterwards. This is a good sign that the training was working well."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "fig, ax = plt.subplots(1, 1, figsize=(6, 4), facecolor=\"w\")\n",
    "ax.plot(KTAs)\n",
    "ax.set_xlabel(\"Training steps\")\n",
    "ax.set_ylabel(\"Kernel-target alignment\")\n",
    "plt.tight_layout()\n",
    "plt.show()\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Again, let us display the accuracy of the trained quantum SVM classifier on the training and testing data. We then plot the decision boundaries below."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Train accuracy: 1.0\n",
      "Test accuracy: 1.0\n"
     ]
    }
   ],
   "source": [
    "print(f\"Train accuracy: {train_acc}\")\n",
    "print(f\"Test accuracy: {test_acc}\")\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "disp_trained = DecisionBoundaryDisplay.from_estimator(\n",
    "    opt_qsvm, X, grid_resolution=30, response_method=\"predict\", alpha=0.5, cmap=cm\n",
    ")\n",
    "disp_trained.ax_.scatter(X_train[:, 0], X_train[:, 1], c=y_train, cmap=cm_bright)\n",
    "disp_trained.ax_.scatter(X_test[:, 0], X_test[:, 1], c=y_test, cmap=cm_bright, marker=\"$\\u25EF$\")\n",
    "plt.show()\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We see that after training, the accuracy on both training and testing data has improved to 100%. From the decision boundaries, we also see that all the data points are now correctly classified. In this case, optimizing the kernel-target alignment indeed helps improve the performance of our quantum kernel! However, it should be noted that in general, having a high kernel-target alignment is only a necessary but not sufficient condition for good performance of a quantum kernel. The optimal alignment will not always bring the optimal kernel accuracy."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Conclusion\n",
    "\n",
    "In this tutorial we saw how to make use of Covalent for a quantum machine learning task, i.e., training a quantum kernel for a SVM classifier based on the kernel-target alignment. It is worth noting that for the toy problem given here, we were able to execute everything on our local machine in a reasonable amount of time. However, as the complexity of the problem increases, more hardware resources are demanded. In such cases, Covalent makes it easy to dispatch electrons or lattices to remote or cloud devices such as high performance computing (HPC) clusters. For more information, please refer to the following Covalent plugins for interfacing with advanced computing hardwares: [Slurm plugin](https://github.com/AgnostiqHQ/covalent-slurm-plugin) and [SSH plugin](https://github.com/AgnostiqHQ/covalent-ssh-plugin)."
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "new",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.13"
  },
  "orig_nbformat": 4,
  "vscode": {
   "interpreter": {
    "hash": "65f23ff11413a1b24e6045f226bbb649c5d31fd62a4c12b34b399c99ac705181"
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
